package com.oritsh.dicom.scu;

import com.oritsh.dicom.exception.ConnectionException;
import com.oritsh.dicom.exception.DisconnectException;
import org.dcm4che.data.UID;
import org.dcm4che.net.*;
import org.dcm4che.net.pdu.AAssociateRQ;
import org.dcm4che.net.pdu.PresentationContext;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * Created with IntelliJ IDEA.
 * User: zarra
 * Date: 14-2-5
 * Time: 下午12:19
 * Copyright Shanghai Orient Rain Information Technology Co.,Ltd.
 */
public abstract class DicomSCU implements AutoCloseable {

    private String aeCalling = "ORITSHSCU";

    private int localPort = 104;

    private boolean isConnect;

    protected Device device;

    protected Association as;

    protected static String[] IVR_LE_FIRST = {
            UID.ImplicitVRLittleEndian,
            UID.ExplicitVRLittleEndian,
            UID.ExplicitVRBigEndian
    };

    public DicomSCU(){

    }

    public DicomSCU(String aeCalling){
        this.aeCalling = aeCalling;
    }

//    public static EnumSet<QueryOption> emptyQueryOptions() {
//        EnumSet<QueryOption> queryOptions = EnumSet.noneOf(QueryOption.class);
//        return queryOptions;
//    }

    static public Association rawConnect(Connection localConn,
                                         Connection remoteConn,
                                         ApplicationEntity aeCalling,
                                         ApplicationEntity aeCalled,
                                         AAssociateRQ associateRQ)
            throws ConnectionException {
        try {
            return aeCalling.connect(localConn,remoteConn, associateRQ);
        } catch (IOException |
                InterruptedException |
                IncompatibleConnectionException |
                GeneralSecurityException e) {
            throw new ConnectionException(e.getMessage());
        }
    }

    abstract protected PresentationContext createPresentationContext();

    protected AAssociateRQ createAAssociateRQ(ApplicationEntity aeCalling,ApplicationEntity  aeCalled){
        AAssociateRQ associateRQ = new AAssociateRQ();
        associateRQ.setCalledAET(aeCalled.getAETitle());
        associateRQ.setCallingAET(aeCalling.getAETitle());
        associateRQ.addPresentationContext(createPresentationContext());
        return associateRQ;
    }

    protected Device createDevice(Connection localConn,ApplicationEntity local){
        Device device = new Device(aeCalling);
        device.addConnection(localConn);
        device.addApplicationEntity(local);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        device.setExecutor(executorService);
        device.setScheduledExecutor(scheduledExecutorService);
        return device;
    }

    public void connect(String remoteHost, int remotePort, String aeCalled)
            throws ConnectionException {
        //`设置本地网络参数`
        Connection localConn = new Connection();
        localConn.setPort(localPort);
        //`设置远程网络参数`
        Connection remoteConn = new Connection();
        remoteConn.setHostname(remoteHost);
        remoteConn.setPort(remotePort);
        //`创建本地AETitle`
        ApplicationEntity local = new ApplicationEntity(aeCalling);
        local.addConnection(localConn);
        //`创建远程AETitle`
        ApplicationEntity remote = new ApplicationEntity(aeCalled);
        remote.addConnection(remoteConn);

        PresentationContext presentationContext = createPresentationContext();

        device = createDevice(localConn,local);

        AAssociateRQ rq = createAAssociateRQ(local,remote);

        Association association = rawConnect(localConn, remoteConn, local, remote, rq);

        this.as = association;
    }

    public void disconnect() throws DisconnectException {
       // logger.info("Close " + this.getClass().getName());
        if (as != null && as.isReadyForDataTransfer()) {
            try {
                as.waitForOutstandingRSP();
                as.release();
            } catch (InterruptedException|IOException e) {
                throw new DisconnectException(e.getMessage());
            }
            // as = null;
        }

        if (device != null) {

            List<Connection> connections = device.listConnections();

            Executor executor  = device.getExecutor();
            ScheduledExecutorService scheduledExecutorService = device.getScheduledExecutor();

            //device.setExecutor(null);
            //device.setScheduledExecutor(null);

            if (executor instanceof ExecutorService){
                ExecutorService executorService = (ExecutorService) executor;
                executorService.shutdown();
            }
            scheduledExecutorService.shutdown();

            device = null;
        }
    }

    @Override
    public void close() throws DisconnectException {
        disconnect();
    }
}
